/**
 * @author phi
 */

(function(){ "use strict"; })();

TM.loadScript("dom", "attribute");
TM.loadScript("dom", "style");
TM.loadScript("dom", "event");
TM.loadScript("dom", "animation");

(function(){
    
    /**
     * @namespace
     * @memberof    TM
     */
    TM.DOM = TM.DOM || {};
    
    // CSS3 用にベンダープレフィックス対応
    TM.DOM.checkStyleProperty = function(name)
    {
        if (TM.DOM.STYLE_LIST[name] === true) {
            return '-'+TM.venderPrefix + name.capitalizeFirstLetter();
        }
        return name;
    };
    
    TM.DOM.STYLE_LIST = {
        "transform": true,
    };
})();

/**
 * 
 */
(function(){
    
    /**
     * TODO: DOMElement って名前に変える予定
     * @lends   TM.DOM.Element.prototype
     */
    var Element = TM.DOM.Element = TM.createClass({
        
        element: null,
        
        /**
         * DOMElement クラス.
         * 標準の HTMLElement をより使いやすくするためのラッパークラスです.
         * @constructs
         * @see         <a href="./../../test/dom/dom-test.html">Test Program</a>.
         */
        init: function() {
            this.set.apply(this, arguments);
        },
        
        /**
         * セッター
         * @param {TM.DOM.Element/String} q DOMElement もしくはクエリ
         */
        set: function(q) {
            if (typeof q == "string") {
                this.element = TM.$query(q);
            }
            else if (q != undefined){
                this.element = q;
            }
            else {
                // デフォルトはドキュメント
                this.element = document;
            }
        },
        
        /**
         * 子供の最後尾に追加
         */
        append: function(child) {
            this.element.appendChild(child.element);
            return this;
        },
        
        /**
         * 子供の先頭に追加
         */
        prepend: function(child) {
            this.element.insertBefore(child.element, this.element.firstChild);
            return this;
        },
        
        /**
         * 自分の後に追加
         */
        after: function(child) {
            this.element.parentNode.insertBefore(child.element, this.element.nextSibling);
            return this;
        },
        
        /**
         * 自分の前に追加
         */
        before: function(child) {
            this.element.parentNode.insertBefore(child.element, this.element);
            return this;
        },
        
        /**
         * 引数に渡された要素に自分を append
         */
        appendTo: function(parent) {
            parent.append(this);
            return this;
        },
        
        /**
         * 引数に渡された要素に自分を prepend
         */
        prependTo: function(parent) {
            parent.prepend(this);
            return this;
        },
        
        clone: function() {
            return TM.DOM.Element(this.element.cloneNode(true));
        },
        
        /**
         * 親から自分を引っぺがす
         */
        remove: function() {
            this.element.parentNode.removeChild(this.element);
            return this;
        },
        
        /**
         * 要素生成
         */
        $create: function(tag, addFuncName) {
            // 要素を生成
            var element = TM.DOM.Element(document.createElement(tag));
            // デフォルトの追加方法は append
            if (!addFuncName) { addFuncName="append"; }
            // 自分の子供として追加
            this[addFuncName](element);
            
            return element;
        },
        
        /**
         * query
         */
        $query: function(query, index) {
            var elm = (index) ?
                this.element.querySelectorAll(query)[index] : 
                this.element.querySelector(query);
            
            return TM.DOM.Element(elm);
        },
        
        /**
         * queryAll
         */
        $queryAll: function(query) {
            return TM.DOM.ElementList(query);
        },
        
        /**
         * 固定化
         */
        fixed: function(x, y, width, height) {
            this.style.set("position", "fixed");
            if (x) this.x = x;
            if (y) this.y = y;
            if (width) this.width = width;
            if (height) this.height = height;
            return this;
        },
        
        /**
         * absolute 化
         */
        absolute: function(x, y, width, height) {
            this.style.set("position", "absolute");
            if (x) this.x = x;
            if (y) this.y = y;
            if (width) this.width = width;
            if (height) this.height = height;
            return this;
        },
        
        /**
         * フルスクリーン化
         */
        fullScreen: function() {
            this.element.webkitRequestFullScreen();
        },
        
        /**
         * 文字列化
         */
        toString: function() {
            return "TM.DOM.Element";
        },
        
    });
    
    Element.create = function(tag) {
        return Element( TM.$create(tag) );
    };
    
    /**
     * html の値
     * @name    html
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("html", {
        "get": function()       { return this.element.innerHTML; },
        "set": function(html)   { this.element.innerHTML = html; }
    });
    
    
    /**
     * value の値
     * @name    value
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("value", {
        "get": function()       { return this.element.value; },
        "set": function(value)   { this.element.value = value; }
    });
    
    
    /**
     * x値
     * @name    x
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("x", {
        "get": function()   { return Number( this.element.style.left.replace("px", '') ); },
        "set": function(x)  { this.element.style.left = x+"px"; }
    });
    
    /**
     * y値
     * @name    y
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("y", {
        "get": function()   { return Number( this.element.style.top.replace("px", '') ); },
        "set": function(y)  { this.element.style.top = y+"px"; }
    });
    
    
    /**
     * 幅
     * @name    width
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("width", {
        "get": function()   { return Number( this.element.style.width.replace("px", '') ); },
        "set": function(w)  { this.element.style.width = w+"px"; }
    });
    
    
    /**
     * 高さ
     * @name    height
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("height", {
        "get": function()   { return Number( this.element.style.height.replace("px", '') ); },
        "set": function(h)  { this.element.style.height = h+"px"; }
    });
    
    
    /**
     * 色
     * @name    color
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("color", {
        "get": function()       { return this.element.style.color; },
        "set": function(color)  { this.element.style.color = color; }
    });
    
    
    /**
     * 背景色
     * @name    backgroundColor
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("backgroundColor", {
        "get": function()       { return this.element.style.backgroundColor; },
        "set": function(color)  { this.element.style.backgroundColor = color; }
    });
    
    /**
     * 表示/非表示
     * @name    visible
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("visible", {
        "get": function()   { return this.element.style.visibility != "hidden"; },
        "set": function(v)  { this.element.style.visibility = (v==true) ? "visible" : "hidden"; }
    });
    
    /**
     * テキスト
     * @name    text
     * @fieldof TM.DOM.Element.prototype
     */
    Element.prototype.accessor("text", {
        "get": function()   { return this.element.innerText; },
        "set": function(v)  { this.element.innerText = v; }
    });
    
    
    Element.prototype.getter("parent", function(){
        return (this.element.parent != undefined) ? TM.DOM.Element(this.element.parent) : null;
    });
    Element.prototype.getter("prev", function(){
        return (this.element.previousSibling != undefined) ? TM.DOM.Element(this.element.previousSibling) : null;
    });
    Element.prototype.getter("next", function(){
        return (this.element.nextSibling != undefined) ? TM.DOM.Element(this.element.nextSibling) : null;
    });
    Element.prototype.getter("children", function(){
        return TM.DOM.ElementList(this.element.children);
    });
    
    
    
    console.assert(!TM.$DOMElement, "名前が競合しています!!", "$DOMElement");
    /**
     * TM.DOM.DOMElement の alias.
     * short hand としてお使いください.
     */
    TM.$DOMElement = TM.DOM.Element;
    
})();



(function(){
    
    /**
     * @scope   TM.DOM.ElementList.prototype
     */
    TM.DOM.ElementList = TM.createClass({
        superClass: Array,
        
        /**
         * TM.DOM.Element 用配列
         * @constructs
         */
        init: function(arr) {
            if (typeof arguments[0] == "string") {
                var query = arguments[0];
                arr = TM.$queryAll(query);
            }
            else if (arr == undefined) {
                return ;
            }
            
            for (var i=0,len=arr.length; i<len; ++i) {
                this.push( TM.DOM.Element(arr[i]) );
            }
        },
        
        toString: function() {
            return "";
        }
    });
    
})();

/*
 * Trans
 */
(function(){
    
    var DOM = TM.DOM.Element;    
    
    /**
     * @lends   TM.DOM.Trans.prototype
     */
    var Trans = DOM.Trans = TM.createClass({
        
        dom: null,
        
        /**
         * @constructs
         */
        init: function(dom) {
            this.dom = dom;
        },
        
        setProp: function(prop) {
            var style = this.dom.element.style;
            var prop_list = [];
            
            for (var key in prop) {
                var name = TM.DOM.checkStyleProperty(key);
                prop_list.push( name.toDash() );
                style[name] = prop[key];
            }
            
            style[Trans.PROPERTY] = prop_list.join(', ');   // none;
            
            return this;
        },
        
        setDuration: function(t) {
            var style = this.dom.element.style;
            style[Trans.DURATION] = t;
            return this;
        },
        
        setTimingFunction: function(func) {
            var style = this.dom.element.style;
            style[Trans.TIMING_FUNCTION] = func;
            return this;
        },
        
        resetProp: function() {
            var style = this.dom.element.style;
            style[Trans.PROPERTY] = "none";
            return this;
        },
        
        setEndFunction: function(fn) {
            this.dom.event.add(Trans.END_EVENT, fn);
            return this;
        },
        
    });
    
    Trans.PROPERTY        = TM.venderPrefix + "TransitionProperty";
    Trans.DURATION        = TM.venderPrefix + "TransitionDuration";
    Trans.TIMING_FUNCTION = TM.venderPrefix + "TransitionTimingFunction";
    Trans.DELAY           = TM.venderPrefix + "TransitionDelay";
    Trans.END_EVENT       = (function(){
        return {
            "webkit": "webkitTransitionEnd",
            "moz"   : "transitionend",
            "o"     : "oTransitionEnd",
        }[TM.venderPrefix];
    })();
    
    DOM.prototype.getter("trans", function(){
        return this._trans || ( this._trans = Trans(this) );
    });
    
})();




/*
 * Key
 */
(function(){
    
    var DOM = TM.DOM.Element;
    
    
    
    /**
     * @lends   TM.DOM.Key.prototype
     */
    var Key = TM.createClass({
        
        dom: null,
        
        funcList        : null,
        downFuncList    : null,
        upFuncList      : null,
        pressFuncList   : null,
        
        
        /**
         * @constructs
         */
        init: function(dom) {
            this.dom = dom;
            this.downFuncList   = {};
            this.upFuncList     = {};
            this.pressFuncList  = {};
            this.funcList       = {
                "down"  : {},
                "up"    : {},
                "press" : {},
            };
            
            var self = this;
            var func_caretor = function(dom, func_list, code_type)
            {
                return function(e){
                    // console.log(code_type, e[code_type]);
                    var fn = func_list[ e[code_type] ];
                    if (fn) {
                        var result = fn.apply(dom, arguments);
                        return result;
                    }
                }
            };
            
            // var func_creator2 = function(dom, type, code_type)
            // {
                // return function(e) {
                    // var fn = self.funcList[type][ e[code_type] ];
                    // if (fn) {
//                         
                    // }
                // }
            // }
            
            this.dom.event.add("keydown", func_caretor(this.dom, this.downFuncList, "keyCode"));
            this.dom.event.add("keyup", func_caretor(this.dom, this.upFuncList, "keyCode"));
            this.dom.event.add("keypress", func_caretor(this.dom, this.pressFuncList, "charCode"));
        },
        
        /**
         * 
         */
        add: function(type, key, fn)
        {
            if (!this.downFuncList[key]) { this.downFuncList[key] = []; }
            this.downFuncList[key].push(fn);
        },
        
        
        /**
         * 
         */
        down: function(key, fn)
        {
            if (typeof(key) == "string") {
                var temp = key;
                // 文字列をキーコードに変換
                key = this.stringToKeyCode(key);
                // 特殊キーが存在した場合は
                var special = this.getSpecial(temp);
                if (special) { fn = this.specialFunction(special, fn); }
            }
            this.downFuncList[key] = fn;
            return this;
        },
        
        
        /**
         * 
         */
        up: function(key, fn)
        {
            if (typeof(key) == "string") {
                key = TM.hotkeys[key];
            }
            this.upFuncList[key] = fn;
            return this;
        },
        
        
        /**
         * 
         */
        press: function(charCode, fn)
        {
            if (typeof(charCode) == "string") {
                charCode = this.stringToCharCode(charCode);
            }
            this.pressFuncList[charCode] = fn;
        },
        
        /**
         * 
         */
        stringToKeyCode: function(str)
        {
            var temp = str.split('+');
            if (temp.length >= 2) { str = temp[1]; }
            return TM.hotkeys[str];
        },
        
        /**
         * charCode 
         */
        stringToCharCode: function(str)
        {
            var temp = str.split('+');
            if (temp.length >= 2) { str = temp[1]; }
            return str.charCodeAt();
        },
        
        /**
         * スペシャルキーを取得
         */
        getSpecial: function(str)
        {
            var temp = str.split('+');
            if (temp.length >= 2) {
                return temp[0];
            }
            return "";
        },
        
        /**
         * 
         */
        specialFunction: function(special, fn)
        {
            // 小文字にする
            special = special.toLowerCase();
            // 関数をラップ
            return function(e) {
                if (e[ special+"Key" ] === true) {
                    return fn.apply(this, arguments);
                }
            }
        },
        
    });
    
    
    DOM.prototype.getter("key", function(){
        return this._key || ( this._key = Key(this) );
    });
    
})();






/*
 * Mouse
 */
(function(){
    
    var DOM = TM.DOM.Element;
    
    
    /**
     * @lends   TM.DOM.Mouse.prototype
     */
    var Mouse = TM.DOM.Mouse = TM.createClass({
        
        dom: null,
        
        prevX   : 0,
        prevY   : 0,
        dX      : 0,
        dY      : 0,
        
        press   : 0,    // 押しているキー
        down    : 0,    // 押したキー
        up      : 0,    // 離したキー
        last    : 0,    // 押していたキー
        
        /**
         * @constructs
         */
        init: function(dom) {
            this.dom = dom;
            this.button = 0;
            
            var self = this;
            this.dom.event.add("mousemove", function(e){
                var rect = e.target.getBoundingClientRect();
                self.x = e.clientX - rect.left;
                self.y = e.clientY - rect.top;
            });

            this.dom.event.add("mousedown", function(e){
                self.button |= 1<<e.button;
            });
            
            this.dom.event.add("mouseup", function(e){
                self.button &= ~(1<<e.button);
            });
        },
        
        update: function() {
            this.last = this.press;
            
            this.press = this.button;
            
            this.down = (this.press ^ this.last) & this.press;
            this.up   = (this.press ^ this.last) & this.last;
            
            this.dX = this.x - this.prevX;
            this.dY = this.y - this.prevY;
            
            this.prevX = this.x;
            this.prevY = this.y;
        },
        
        setName: function(name) {
            this.dom.element.style["webkitAnimationName"] = name;
            return this;
        },
        
        getButton: function(button) {
            return this.press & button;
        },
        
        getButtonDown: function(button) {
            return this.down & button;
        },
        
        getButtonUp: function(button) {
            return this.up & button;
        },
        
    });
    
    Mouse.BUTTON_LEFT   = 0x1;
    Mouse.BUTTON_MIDDLE = 0x2;
    Mouse.BUTTON_RIGHT  = 0x4;
    
    DOM.prototype.getter("mouse", function(){
        return this._mouse || ( this._mouse = Mouse(this) );
    });
    
})();




